package org.vaadin.nf4v.core.util;

import org.vaadin.nf4v.core.navigation.target.NavigationTarget;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * Utility class to easily access navigation request parameters.
 * Parameters are preparsed by ParamResolver implementation, so parameters may be of any type
 * (depends on resolver implementation)
 * Created internally in NavigationRequest and could be accessed through NavigationRequest#params() method.
 */
public class Params implements Serializable{
    private NavigationTarget target;
    private Map<Integer, String> keysIndex = new HashMap<Integer, String>();

    // todo redesign params handling to remove not obvious exception

    public Params(NavigationTarget target) {
        this.target = target;
        int i = 1;
        if (target.getParams() != null) {
            for (String key : target.getParams().keySet()) {
                keysIndex.put(i++, key);
            }
        }
    }

    /**
     *
     * @param key param key
     * @return parameter object, associated with provided key, or null if no parameter is associated with key
     */
    @SuppressWarnings("unchecked")
    public <T> T getTypeParam(String key) {
        return (T) target.getParams().get(key);
    }

    /**
     *
     * @param position parameter position (either in url if request is initiated by browser url change
     * or index in order of addition of parameters through api call)
     * @return parameter object, associated with provided position, or null if no parameter is associated with position
     */
    @SuppressWarnings("unchecked")
    public <T> T getTypeParam(int position) {
        if (target.getParams().size() > position) {
            return null;
        }
        return (T) keysIndex.get(position) != null ? this.<T>getTypeParam(keysIndex.get(position)) : null;
    }

    /**
     *
     * @param key param key
     * @return parameter string, associated with provided key, or null if no parameter is associated with key
     */
    public String getStringParam(String key) {
        Object obj = getTypeParam(key);
        return obj == null ? null : obj.toString();
    }

    /**
     *
     * @param position parameter position
     * @return parameter string, associated with provided position, or null if no parameter is associated with position
     */
    public String getStringParam(int position) {
        if (target.getParams().size() > position) {
            return null;
        }
        return keysIndex.get(position) != null ? getStringParam(keysIndex.get(position)) : null;
    }

    /**
     *
     * @param key param key
     * @return parameter long, associated with provided key, or null if no parameter is associated with key
     */
    public Long getLongParam(String key) {
        Object obj = getTypeParam(key);
        if (obj instanceof Long) {
            return (Long) obj;
        }
        if (obj instanceof Number) {
            return (Long) obj;
        }
        return obj == null ? null : Long.parseLong(getStringParam(key));
    }

    /**
     *
     * @param position parameter position
     * @return parameter long, associated with provided position, or null if no parameter is associated with position
     */
    public Long getLongParam(int position) {
        Object obj = getTypeParam(position);
        if (obj instanceof Long) {
            return (Long) obj;
        }
        if (obj instanceof Number) {
            return (Long) obj;
        }
        return obj == null ? null : Long.parseLong(getStringParam(position));
    }

    // todo other methods
}
